package com.idealighter.game.games.errentexaspoker.manager;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Inject;
import com.google.inject.Singleton;

import com.idealighter.game.common.CommonMsgMgr;
import com.idealighter.game.core.common.Game;
import com.idealighter.game.core.constant.Operator;
import com.idealighter.game.core.error.ErrorCode;
import com.idealighter.game.dblog.service.DbLogService;
import com.idealighter.game.dictionary.dic.ErrentexaporkerRoomDic;
import com.idealighter.game.dictionary.dic.ErrentexaporkerTimeDic;
import com.idealighter.game.dictionary.domain.ErrentexaporkerRoomDomain;
import com.idealighter.game.event.manager.EventMgr;
import com.idealighter.game.event.struct.ClearGameDataEvent;
import com.idealighter.game.event.struct.PlayerExitEvent;
import com.idealighter.game.event.struct.ShutdownRoomEvent;
import com.idealighter.game.event.struct.StartRoomEvent;
import com.idealighter.game.gamehall.event.EnterRoomEvent;
import com.idealighter.game.gamehall.event.EnterTableEvent;
import com.idealighter.game.gamehall.event.ExitRoomEvent;
import com.idealighter.game.gamehall.event.ExitTableEvent;
import com.idealighter.game.gamehall.event.RoomMemInfoUpdateEvent;
import com.idealighter.game.gamehall.manager.GameHallMgr;
import com.idealighter.game.games.common.AbstractRoom;
import com.idealighter.game.games.common.SeatState;
import com.idealighter.game.games.errentexaspoker.struct.ErrenTexapokerRoom;
import com.idealighter.game.games.errentexaspoker.struct.ErrenTexapokerRoom.Table;
import com.idealighter.game.games.errentexaspoker.struct.ErrenTexapokerRoom.Table.Seat;
import com.idealighter.game.games.texaspoker.struct.BettingDecision;
import com.idealighter.game.games.texaspoker.struct.cards.Card;
import com.idealighter.game.games.texaspoker.util.HandPower;
import com.idealighter.game.games.texaspoker.util.HandPowerRanker;
import com.idealighter.game.log.core.LogReason;
import com.idealighter.game.log.struct.game.Game15WinLoseLog;
import com.idealighter.game.log.struct.game.GameAfeeLog;
import com.idealighter.game.login.manager.LoginMgr;
import com.idealighter.game.player.manager.PlayerMgr;
import com.idealighter.game.player.service.bo.PlayerBo;
import com.idealighter.game.player.struct.Player;
import com.idealighter.game.schedule.manager.ScheduleMgr;
import com.idealighter.game.server.ExecutorMgr;
import com.idealighter.game.server.core.executor.DisruptorExecutor;
import com.idealighter.utils.code.RandCodeUtil;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 二人德州扑克逻辑 .
 */
@Singleton
public class ErrenTexapokerMgr {

  private static final Logger LOG = LoggerFactory.getLogger(ErrenTexapokerDataMgr.class);

  // 手牌
  public static final int STARTING_HAND = 2;
  // 三次发底牌数量
  public static final int BOARD_CARD = 3;
  public static final int BOARD_CARD_TURN = 1;
  public static final int BOARD_CARD_RIVER = 1;
  public static final int COMMON_CARDS_NUM = 5;
  private static final int ADDED_DELAY = 2;
  private final HandPowerRanker handPowerRanker = new HandPowerRanker();

  private final EventMgr eventMgr;
  @Inject
  private ErrenTexapokerDataMgr dataMgr;
  @Inject
  private ErrenTexapokerMsgMgr msgMgr;
  @Inject
  private PlayerMgr playerMgr;
  @Inject
  private ScheduleMgr scheduleMgr;
  @Inject
  private ErrentexaporkerRoomDic roomDic;
  @Inject
  private CommonMsgMgr commonMsgMgr;
  @Inject
  private ErrentexaporkerTimeDic timeDic;
  @Inject
  private GameHallMgr gameHallMgr;
  @Inject
  private LoginMgr loginMgr;
  @Inject
  private ErrenTexapokerControlScript errenTexapokerControl;
  // 游戏线程executor,可以执行普通任务和定时任务
  private final DisruptorExecutor gameExecutor;

  /**
   * 构造函数.
   * 
   * @param eventMgr .
   * @param executorMgr .
   */
  @Inject
  public ErrenTexapokerMgr(EventMgr eventMgr, ExecutorMgr executorMgr) {
    this.eventMgr = eventMgr;
    eventMgr.register(this);
    this.gameExecutor = executorMgr.getGameExecutor(Game.ERREN_TEXAPOKER.getModuleId());
  }

  /**
   * 订阅玩家退出游戏事件 .
   * 
   * @param event .
   */
  @Subscribe
  public void onPlayerExitGame(PlayerExitEvent event) {
    gameExecutor.execute(() -> {
      Player player = event.player;
      if (dataMgr.getPlayerSeat(player.getId()) != null) {
        exitTable(player);
      }
      if (player.curRoom instanceof ErrenTexapokerRoom) {
        exitRoom(player);
      }
    });
  }

  /**
   * .
   * 
   * @param event .
   */
  @Subscribe
  public void onStartRoom(StartRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.ERREN_TEXAPOKER) {
        roomDic.load();
      }
    });
  }

  /**
   * .
   * 
   * @param event .
   */
  @Subscribe
  public void onShutDownRoom(ShutdownRoomEvent event) {
    gameExecutor.execute(() -> {
      if (event.getGame() == Game.ERREN_TEXAPOKER) {
        roomDic.load();
        AbstractRoom room = dataMgr.getRoom(event.getRoomId());
        for (Long playerId : room.getPlayers()) {
          Player player = playerMgr.getPlayer(playerId);
          if (player != null) {
            loginMgr.noticeLogoutForGameExit(player);
          }
        }
      }

    });
  }

  @Subscribe
  public void onClearGameData(ClearGameDataEvent event) {
    gameExecutor.execute(() -> doClearGameData(event.playerId));
  }

  /**
   * 进入指定房间.
   * 
   * @param player 玩家.
   * @param roomId .
   */
  public void enterRoom(Player player, int roomId) {
    AbstractRoom curRoom = player.curRoom;
    if (curRoom != null) {
      Game game = curRoom.game();
      LOG.warn("[二人德州]玩家[{}][{}]当前在游戏[{}]房间[{}]中不能进入房间", player.getPlayerName(), player.getId(),
          game.getDesc(), roomId);
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ALREADY_IN, Operator.SYSTEM,
          game.getDesc());
      return;
    }

    ErrenTexapokerRoom room = dataMgr.getRoom(roomId);
    if (room == null) {
      LOG.error("[二人德州]玩家[{}][{}]进入的房间[{}]不存在", player.getPlayerName(), player.getId(), roomId);
      return;
    }

    ErrentexaporkerRoomDomain roomDom = roomDic.map().get(roomId);
    if (roomDom.getIsActive() != 1) {
      LOG.error("[二人德州]玩家[{}][{}]进入的房间[{}]已经关闭", player.getPlayerName(), player.getId(), roomId);
      return;
    }
    if (player.getGold() < roomDom.getLower()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_BELOW_MIN_GOLD, Operator.SYSTEM,
          "" + roomDom.getLower());
      LOG.warn("[二人德州]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(),
          roomId, roomDom.getLower());
      return;
    }

    if (player.getGold() > roomDom.getUpper()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_BEYOND_MAX_GOLD, Operator.SYSTEM,
          "" + roomDom.getUpper());
      LOG.warn("[二人德州]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(),
          roomId, roomDom.getUpper());
      return;
    }

    if (!player.vip()
        && room.getPlayers().size() >= roomDom.getMaxNum() * roomDom.getOrdinarPeople() / 100) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_NOT_VIP_ROOM_FULL, Operator.SYSTEM);
      LOG.warn("[二人德州]玩家[{}][{}]进入的房间[{}]普通玩家人数已超过上限", player.getPlayerName(), player.getId(),
          roomId, roomDom.getMaxNum());
      return;
    }

    if (room.getPlayers().size() >= roomDom.getMaxNum()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ROOM_FULL, Operator.SYSTEM);
      LOG.warn("[二人德州]玩家[{}][{}]进入的房间[{}]已超过最大人数[{}]", player.getPlayerName(), player.getId(),
          roomId, roomDom.getMaxNum());
      return;
    }
    player.curRoom = room;
    room.getPlayers().add(player.getId());
    msgMgr.sendEnterRoomMsg(player);
    LOG.info("[二人德州]玩家[{}][{}]进入房间[{}]成功", player.getPlayerName(), player.getId(), roomId);
    eventMgr.post(new EnterRoomEvent(player, room));
  }

  /**
   * 进入指定牌桌指定位置.
   * 
   * @param player 玩家.
   * @param tableId . ->唯一的貌似
   * @param order .
   */
  public void enterTable(Player player, int tableId, int order, String tablePwd) {
    ErrenTexapokerRoom.Table table = dataMgr.getTable(tableId);

    if (table == null) {
      LOG.error("[二人德州]玩家[{}][{}]进入的牌桌[{}]不存在", player.getPlayerName(), player.getId(), tableId);
      return;
    }

    if (!gameHallMgr.checkTableSetting(player, table, tablePwd)) {
      return;
    }
    if (dataMgr.getPlayerSeat(player.getId()) != null) {
      LOG.error("[二人德州]玩家[{}][{}]已经在牌桌中不能重复进入牌桌", player.getPlayerName(), player.getId());
      return;
    }

    if (table.isGaming) {
      LOG.error("[二人德州]玩家[{}][{}]进入的牌桌[{}]正在游戏中，不能进入", player.getPlayerName(), player.getId(),
          tableId);
      return;
    }

    if (order < 1 || order > table.seats.size()) {
      LOG.error("[二人德州]玩家[{}][{}]进入的牌桌位置[]不合法", player.getPlayerName(), player.getId(), order);
      return;
    }

    ErrenTexapokerRoom.Table.Seat seat = table.seats.get(order - 1);
    if (seat == null) {
      LOG.info("order Wrong");
      return;
    }
    // 该位置是否有人
    if (seat.getPlayerId() > 0) {
      // msgMgr.sendEnterTableMsg(player, table, 1, seat);
      LOG.error("[二人德州]玩家[{}][{}]进入的牌桌位置[]失败，该座位已经有人", player.getPlayerName(), player.getId(),
          order);
      return;
    }

    int roomId = table.room.getId().intValue();
    ErrentexaporkerRoomDomain roomDom = roomDic.get(roomId);
    if (player.getGold() < roomDom.getLower()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_NOT_ENOUGH, Operator.SYSTEM);
      LOG.warn("[二人德州]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(),
          roomId, roomDom.getLower());
      return;
    }

    if (player.getGold() > roomDom.getUpper()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD, Operator.SYSTEM);
      LOG.warn("[二人德州]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(),
          roomId, roomDom.getUpper());
      return;
    }
    // 台费(金币)
    int afee = roomDic.map().get(table.room.getId().intValue()).getAfee();
    if (player.getGold() < afee) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_NOT_ENOUGH_FEE, Operator.SYSTEM);
      LOG.warn("[二人德州]玩家[{}][{}]进入德州扑克桌子金币不足台费", player.getPlayerName(), player.getId());
      return;
    } else {
      playerMgr.minusGold(player, afee, LogReason.ERREN_TEXAPOKER_ENTER_TABLE_MINUS_AFEE);
      DbLogService.log(new GameAfeeLog(player, table, afee));
    }

    int readyTime = timeDic.get(1).getTime();
    seat.stepFuture = scheduleMgr.schedule(() -> exitTable(player), readyTime + ADDED_DELAY,
        TimeUnit.SECONDS, gameExecutor);
    seat.setPlayerId(player.getId());
    seat.setPlayerName(player.getPlayerName());
    seat.setState(SeatState.SEATED);
    seat.setRobot(player.playerBo().isRobot());
    dataMgr.updatePlayerSeats(player.getId(), seat);
    eventMgr.post(new EnterTableEvent(player, seat));
    msgMgr.sendEnterTableMsg(player, table);
    msgMgr.sendOhterPlayer(table, seat);
    LOG.info("[二人德州]玩家[{}][{}]进入桌子[{}]座位[{}]成功", player.getPlayerName(), player.getId(), tableId,
        order);
  }

  /**
   * 玩家准备.
   * 
   * @param player 玩家.
   */
  public void ready(Player player) {
    // 座位
    ErrenTexapokerRoom.Table.Seat seat = dataMgr.getPlayerSeat(player.getId());

    if (seat == null) {
      LOG.error("[二人德州]玩家[{}][{}]还未进入牌桌不能准备", player.getPlayerName(), player.getId());
      return;
    }
    if (seat.ready) {
      LOG.warn("[二人德州]玩家[{}][{}]已经准备好,请勿重复准备", player.getPlayerName(), player.getId());
      msgMgr.sendReadyMsg(seat, 1);
      return;
    }
    if (seat.getTotalChips() < roomDic.map().get(seat.table.room.getId().intValue()).getMinOne()) {
      LOG.warn("[二人德州]玩家[{}][{}]身上的筹码不够开始该桌游戏", player.getPlayerName(), player.getId());
      return;
    }
    if (seat.table.isGaming) {
      LOG.warn("[二人德州]该桌[{}]游戏已经开始，玩家[{}][{}]无法准备", seat.table.getId(), player.getPlayerName(),
          player.getId());
      return;
    }
    seat.stepFuture.cancel(false);
    seat.ready = true;
    seat.setState(SeatState.READY);
    minusChips(seat, 0);// 同步筹码
    final ErrenTexapokerRoom.Table table = seat.table;
    msgMgr.sendReadyMsg(seat, 0);
    LOG.info("[二人德州]玩家[{}][{}]准备好", player.getPlayerName(), player.getId());
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
    readyDealCards(table, player, false);
  }

  /**
   * 准备好后发牌.
   *
   * @param table .
   */
  private void readyDealCards(Table table, Player player, boolean exit) {
    boolean allReady = true;
    if (!exit) {
      table.readyCount += 1;
    }
    for (ErrenTexapokerRoom.Table.Seat s : table.seats) {
      if (s.getPlayerId() > 0) {
        if (!s.ready) {
          allReady = false;
          break;
        }
      }
    }
    if (allReady && table.readyCount > 1) {
      LOG.info("[二人德州]牌桌[{}]玩家全部准备好,开始洗牌发牌", table.getId());
      reshuffleCards(table);
      errenTexapokerControl.controlDealCards(table);
      // dealCards(table);
      table.isGaming = true;
      table.state = 0;
      table.readyCount = 0;
      table.isdealCardOver = false;
      for (Long playerId : table.players()) {
        Seat playerSeat = dataMgr.getPlayerSeat(playerId);
        playerSeat.setState(SeatState.GAMING);
        Player player1 = playerMgr.getPlayer(playerId);
        eventMgr.post(new RoomMemInfoUpdateEvent(player1));
      }
    }
  }

  /**
   * 处理玩家通知发牌结束消息.
   *
   * @param player 玩家.
   * @param roomId .
   * @param tableId .
   */
  public void dodealCardOver(Player player, int roomId, int tableId) {
    if (player == null) {
      return;
    }
    ErrenTexapokerRoom.Table.Seat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      return;
    }
    ErrenTexapokerRoom.Table table = seat.table;
    if (table.isdealCardOver) {
      return;
    }
    table.isdealCardOver = true;

    if (table.isBanlance) {
      doBalance(table);
      return;
    }
    // 发牌结束通知第一个人下注
    if (table.curOrder < 1) {
      return;
    }
    notifyNextSeat(table.seats.get(table.curOrder - 1));

  }

  /**
   * 玩家请求下一步操作.
   *
   * @param player 玩家.
   */
  public void dealNextStep(Player player) {
    if (player == null) {
      return;
    }
    ErrenTexapokerRoom.Table.Seat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      return;
    }
    ErrenTexapokerRoom.Table table = seat.table;
    if (table == null) {
      return;
    }
    if (checkIsAllFlop(table)) {
      doFlopBalance(table);
      return;
    }
    if (checkBets(table) && checkBettingRoud(table)) {
      resetBetingRound(table);
      saveBetRecord(table);
      if (checkIsAllIn(table)) {
        if (table.state == 1) {
          dealAllCommonCards(table, table.state);
          table.isBanlance = true;
          return;
        }
        if (table.state == 2) {
          dealTurnAndRiver(table, table.state);
          table.isBanlance = true;
          return;
        }
        if (table.state == 3) {
          dealRiver(table, table.state);
          table.isBanlance = true;
          return;
        }
      }
      if (table.state == 1) {
        dealFlop(table, table.state);
        return;
      } else if (table.state == 2) {
        dealTurn(table, table.state);
        return;
      } else if (table.state == 3) {
        dealRiver(table, table.state);
        return;
      } else if (table.state == 4) {
        doBalance(table);
        return;
      }
    }
    notifyNextSeat(seat);
  }

  /**
   * 重新洗该牌桌的牌.
   * 
   * @param table .
   */
  private void reshuffleCards(ErrenTexapokerRoom.Table table) {
    /*
     * 清空玩家手上的牌 .
     */
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      seat.cards.clear();
    }

    /*
     * 打乱两次牌 .
     */
    Collections.shuffle(table.cards);
    Collections.shuffle(table.cards);
  }

  /**
   * 牌排序 .
   * 
   * 
   * @param table .
   * @param winner .
   * @param loser .
   * @param robot .
   */
  private void sortCards(Table table, List<Integer> winner, List<Integer> robot,
      List<Integer> loser) {
    LinkedList<List<Card>> sortedCardsList = new LinkedList<>();
    for (int i = 1; i <= table.seats.size(); i++) {
      List<Card> cards = Lists.newArrayList();
      cards.addAll(table.cards.subList(table.cardIndex, table.cardIndex += STARTING_HAND));
      cards.addAll(table.commonCards);
      sortedCardsList.add(cards);
    }
    sortedCardsList.sort((cards1, cards2) -> {
      HandPower handPower1 = handPowerRanker.rank(cards1);
      HandPower handPower2 = handPowerRanker.rank(cards2);
      if (handPower1.compareTo(handPower2) < 0) {
        return 1;
      } else {
        return -1;
      }
    });

    /**
     * 赢家.
     */

    for (Integer order : winner) {
      for (Seat seat : table.seats) {
        if (seat.getOrder() == order) {
          seat.cards.addAll(sortedCardsList.removeFirst());
        }
      }
    }
    /**
     * 机器人.
     */
    for (Integer order : robot) {
      for (Seat seat : table.seats) {
        if (seat.getOrder() == order) {
          seat.cards.addAll(sortedCardsList.removeFirst());
        }
      }
    }
    /**
     * 输家 .
     */
    for (Integer order : loser) {
      for (Seat seat : table.seats) {
        if (seat.getOrder() == order) {
          seat.cards.addAll(sortedCardsList.removeFirst());
        }
      }
    }

  }

  /**
   * 发手牌,每人先发2张牌.
   * 
   * @param table .
   * @param winner .
   * @param robot .
   * @param loser .
   */
  public void dealCards(ErrenTexapokerRoom.Table table, List<Integer> winner, List<Integer> robot,
      List<Integer> loser) {
    List<Card> cards = table.cards;
    if (table.cardIndex != 0) {
      table.cardIndex = 0;
    }
    table.commonCards.addAll(cards.subList(table.cardIndex, table.cardIndex += COMMON_CARDS_NUM));
    table.seats.stream().filter(seat -> seat.getPlayerId() > 0).forEach(seat -> {
      seat.resetButReady();
    });
    if (winner.isEmpty() && robot.isEmpty() && loser.isEmpty()) {
      table.seats.stream().filter(seat -> seat.getPlayerId() > 0).forEach(seat -> {
        seat.cards.addAll(cards.subList(table.cardIndex, table.cardIndex += STARTING_HAND));
      });

    } else {
      sortCards(table, winner, robot, loser);
    }
    winResult(table);
    table.nextCallOrder = randLandorSeat(table);
    int minOne = roomDic.map().get(table.room.getId().intValue()).getMinOne();
    table.landlordOrder = (byte) randLandorSeat(table);
    table.smallBind = (byte) table.nextSeatOrder(table.landlordOrder);
    minusChips(table.seats.get(table.smallBind - 1), minOne / 2);
    table.seats.get(table.smallBind - 1).bet = minOne / 2;
    table.seats.get(table.smallBind - 1).curBet = minOne / 2;
    table.seats.get(table.smallBind - 1).betList.add(minOne / 2);
    table.bigBind = (byte) table.nextSeatOrder(table.smallBind);
    minusChips(table.seats.get(table.bigBind - 1), minOne);
    table.seats.get(table.bigBind - 1).bet = minOne;
    table.seats.get(table.bigBind - 1).curBet = minOne;
    table.seats.get(table.bigBind - 1).betList.add(minOne);
    table.maxBet = minOne;
    table.raiseBet = minOne / 2;
    table.totalChips = minOne + minOne / 2;
    LOG.info("[二人德州]庄家位置[{}], 大盲注位置[{}], 小盲注位置[{}]", table.landlordOrder, table.bigBind,
        table.smallBind);
    msgMgr.sendDealCardsMsg(table);
    table.curOrder = table.bigBind;
    table.nextPlayOrder = table.nextSeatOrder(table.curOrder);
  }

  /**
   * 随机庄家位置.
   *
   * @param table .
   * @return
   */
  public int randLandorSeat(ErrenTexapokerRoom.Table table) {
    int rand = RandCodeUtil.random(1, 2);
    ErrenTexapokerRoom.Table.Seat rdseat = table.seats.get(rand - 1);
    if (rdseat.getPlayerId() > 0) {
      return rdseat.getOrder();
    }
    while (true) {
      rand += 1;
      if (rand > table.seats.size()) {
        rand -= table.seats.size();
      }
      rdseat = table.seats.get(rand - 1);
      if (rdseat.getPlayerId() > 0) {
        return rdseat.getOrder();
      }
    }
  }

  /**
   * 玩家下注.
   *
   * @param player 玩家.
   * @param roomId .
   * @param tableId .
   * @param bet .
   */
  public void playerGame(Player player, int roomId, int tableId, long bet, int type) {
    if (player == null) {
      return;
    }
    ErrenTexapokerRoom.Table.Seat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null || bet < 0) {
      LOG.error("[二人德州]玩家[{}][{}]下注失败", player.getPlayerName(), player.getId());
      return;
    }
    if (seat.getOrder() == seat.table.curOrder) {
      LOG.error("[二人德州]玩家[{}][{}]不能重复下注", player.getPlayerName(), player.getId());
      return;
    }
    if (!seat.table.isGaming) {
      LOG.error("[二人德州]本桌[{}]还未开始，不能下注", seat.table.getId());
      return;
    }
    if (seat.getOrder() != seat.table.nextPlayOrder) {
      LOG.error("[二人德州]还未轮到玩家[][]下注", player.getPlayerName(), player.getId());
      return;
    }
    if (seat.getTotalChips() < bet) {
      LOG.error("[二人德州]玩家[{}][{}]下注失败,筹码不够 [{}], 类型[{}]", player.getPlayerName(), player.getId(),
          bet, type);
      return;
    }
    ErrenTexapokerRoom.Table table = seat.table;
    LOG.info("[二人德州]玩家[{}][{}]开始下注位置[{}]，下注筹码[{}]", seat.getPlayerId(), seat.getPlayerName(),
        seat.getOrder(), bet);
    if (BettingDecision.getBettingType(type) == BettingDecision.CALL) {
      long needBet = table.maxBet - seat.curBet;
      if (bet < needBet) {
        LOG.warn("[二人德州]玩家[{}][{}]筹码不对,跟注失败。需要[{}],实际[{}]", seat.getPlayerId(),
            seat.getPlayerName(), needBet, bet);
        return;
      }
      if (bet == seat.getTotalChips()) {
        seat.isAllIn = true;
      }
    } else if (BettingDecision.getBettingType(type) == BettingDecision.FOLD) {
      seat.isFold = true;
    } else if (BettingDecision.getBettingType(type) == BettingDecision.RAISE) {
      long needBet = table.maxBet + table.raiseBet - seat.curBet;
      if (bet < needBet) {
        LOG.warn("[二人德州]玩家[{}][{}]加注失败，筹码不够需要[{}],实际[{}]", seat.getPlayerId(), seat.getPlayerName(),
            needBet, bet);
        return;
      }
      table.raiseBet += bet - needBet;
      table.maxBet = seat.curBet + bet;
    } else if (BettingDecision.getBettingType(type) == BettingDecision.ALL_IN) {
      if (bet < seat.getTotalChips()) {
        LOG.warn("[二人德州]玩家[{}][{}]需要[{}]   实际[{}]", seat.getPlayerId(), seat.getPlayerName(),
            seat.getTotalChips(), bet);
        return;
      }
      seat.isAllIn = true;
      if (seat.curBet + bet > table.maxBet) {
        long needBet = table.maxBet + table.raiseBet - seat.curBet;
        if (bet > needBet) {
          table.raiseBet += bet - needBet;
        }
        table.maxBet = seat.curBet + bet;
      }
    } else if (BettingDecision.getBettingType(type) == BettingDecision.CHECK) {
      if (bet != 0 && seat.curBet != table.maxBet) {
        LOG.warn("[二人德州]玩家[{}][{}]让牌失败需要0,实际[{}].MaxBet: [{}]， RaiseBet[{}]", seat.getPlayerName(),
            seat.getTotalChips(), bet, table.maxBet, table.raiseBet);
        return;
      }
    }
    if (bet == seat.getTotalChips()) {
      seat.isAllIn = true;
      type = 3;
    }
    seat.stepFuture.cancel(false);
    LOG.info("[二人德州]玩家[{}][{}]下注完成", seat.getPlayerId(), seat.getPlayerName());
    seat.isBet = true;
    seat.curBet += bet;
    seat.bet += bet;
    minusChips(seat, bet);
    table.totalChips += bet;
    table.curOrder = seat.getOrder();
    if (bet == 0 && type != 1) {
      type = 4;
    }
    msgMgr.sendBetMsg(table, type, bet, seat.getOrder());// 发送下注消息给其他玩家
  }

  /**
   * 通知下一家.
   *
   * @param seat 座位信息.
   */
  public void notifyNextSeat(ErrenTexapokerRoom.Table.Seat seat) {
    if (!seat.table.isGaming) {
      LOG.error("[二人德州]桌子[{}]还未开始，不能下注", seat.table.getId());
      return;
    }
    int nextOrder = seat.table.nextSeatOrder(seat.getOrder());
    seat.table.nextPlayOrder = nextOrder;
    ErrenTexapokerRoom.Table.Seat nextSeat = null;
    for (ErrenTexapokerRoom.Table.Seat seat1 : seat.table.seats) {
      if (seat1.getOrder() == nextOrder) {
        nextSeat = seat1;
        break;
      }
    }
    final ErrenTexapokerRoom.Table.Seat finalNextSeat = nextSeat;
    int readyTime = timeDic.get(2).getTime();
    nextSeat.stepFuture = scheduleMgr.schedule(() -> doFold(finalNextSeat, false),
        readyTime + ADDED_DELAY, TimeUnit.SECONDS, gameExecutor);
    // 通知下一个玩家
    msgMgr.sendNextSeatBet(seat.table, nextOrder, seat.table.raiseBet, seat.table.maxBet);
  }

  /**
   * 超过时间没有下注，作弃牌处理.
   *
   * @param seat 座位信息.
   * @param isExit .
   */
  public void doFold(Seat seat, boolean isExit) {
    seat.curBet = 0;
    seat.isFold = true;
    msgMgr.sendBetMsg(seat.table, 1, 0, seat.getOrder());
    if (isExit) {
      if (checkIsAllFlop(seat.table)) {
        /**
         * 踢掉机器人后弃牌结算 .
         */
        doFlopBalance(seat.table);
        return;
      }
      if (seat.getOrder() == seat.table.nextPlayOrder) {
        notifyNextSeat(seat);
      }
    }
  }

  /**
   * 检查该轮是否所有人都进行了一次操作- . > 若是只剩下一个人则不用进行该项检查.
   *
   * @param table .
   */
  public boolean checkBettingRoud(ErrenTexapokerRoom.Table table) {
    boolean flag = true;
    int count = 0;
    for (Seat seat : table.seats) {
      if (seat.getPlayerId() > 0 && !seat.isFold && !seat.isAllIn) {
        count += 1;
      }
    }
    if (count == 1) {
      return true;
    }
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (seat.getPlayerId() > 0 && seat.isFold == false && seat.isAllIn == false
          && seat.isBet == false) {
        flag = false;
        break;
      }
    }
    return flag;
  }

  /**
   * 重置下注状态.
   *
   * @param table .
   */
  public void resetBetingRound(ErrenTexapokerRoom.Table table) {
    table.state += 1;
    table.maxBet = 0;
    table.raiseBet = 0;
    table.isdealCardOver = false;
    table.seats.stream().filter(seat -> seat.getPlayerId() > 0).forEach(seat -> {
      seat.isBet = false;
    });
  }

  /**
   * 检查每个玩家下注是否相等.
   *
   * @param table .
   */
  public boolean checkBets(ErrenTexapokerRoom.Table table) {
    boolean flag = true;
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (seat.getPlayerId() > 0 && seat.isFold == false && seat.isAllIn == false) {
        for (ErrenTexapokerRoom.Table.Seat seat1 : table.seats) {
          if (seat1.getPlayerId() > 0 && seat1.isFold == false) {
            if (seat.getOrder() == seat1.getOrder()) {
              continue;
            }
            if (seat.bet != seat1.bet) {
              if (seat1.isAllIn) {
                if (seat.bet > seat1.bet) {
                  continue;
                } else {
                  flag = false;
                  break;
                }
              }
              flag = false;
              break;
            }
          }
        }
      }
    }
    return flag;
  }

  /**
   * 记录玩家每一轮的下注数.
   *
   * @param table .
   */

  public void saveBetRecord(ErrenTexapokerRoom.Table table) {
    table.seats.stream().filter(seat -> seat.getPlayerId() > 0).forEach(seat -> {
      seat.betList.add(seat.curBet);
      seat.curBet = 0;
    });

  }

  /**
   * 检查是否所有人弃牌，如果所有人都弃牌直接结算.
   *
   * @param table .
   */
  public boolean checkIsAllFlop(ErrenTexapokerRoom.Table table) {
    int totalnum = 0;
    int numFlop = 0;
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (seat.getPlayerId() > 0) {
        totalnum++;
      }
    }
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (seat.getPlayerId() > 0 && seat.isFold) {
        numFlop++;
      }
    }
    if (numFlop >= totalnum - 1) {
      return true;
    }
    return false;
  }

  /**
   * 检查是否所有人都梭哈.
   *
   * @param table .
   */
  public boolean checkIsAllIn(ErrenTexapokerRoom.Table table) {
    int totalnum = 0;
    int numFlop = 0;
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (seat.getPlayerId() > 0 && seat.isFold == false) {
        totalnum++;
      }
    }
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (seat.getPlayerId() > 0 && seat.isAllIn) {
        numFlop++;
      }
    }
    if (numFlop >= totalnum - 1) {
      return true;
    }
    return false;
  }

  /**
   * 是否有人梭哈.
   *
   * @param table .
   * @return
   */
  public boolean checkIsHaveAllIn(ErrenTexapokerRoom.Table table) {
    boolean flag = false;
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (seat.getPlayerId() > 0 && seat.isAllIn == true) {
        flag = true;
      }
    }
    return flag;
  }

  /**
   * 发公共牌三张 .
   */

  public void dealFlop(ErrenTexapokerRoom.Table table, int state) {
    if (table.commonCards.size() != 5) {
      LOG.error("[二人德州]桌子[{}]底牌异常", table.getId());
      return;
    }
    List<Card> cardList = Lists.newArrayList();
    cardList.addAll(table.commonCards.subList(0, 3));
    msgMgr.sendCommonCard(table, state, cardList, 0);
  }

  /**
   * 发第4张牌 .
   */
  public void dealTurn(ErrenTexapokerRoom.Table table, int state) {
    if (table.commonCards.size() != 5) {
      LOG.error("[二人德州]桌子[{}]底牌异常", table.getId());
      return;
    }
    List<Card> cardList = Lists.newArrayList();
    cardList.addAll(table.commonCards.subList(3, 4));
    msgMgr.sendCommonCard(table, state, cardList, 0);
  }

  /**
   * 发第五张牌 .
   */
  public void dealRiver(ErrenTexapokerRoom.Table table, int state) {
    if (table.commonCards.size() != 5) {
      LOG.error("[二人德州]桌子[{}]底牌异常", table.getId());
      return;
    }
    List<Card> cardList = Lists.newArrayList();
    cardList.addAll(table.commonCards.subList(4, 5));
    msgMgr.sendCommonCard(table, state, cardList, 0);
  }

  /**
   * 一次性发5张公共牌 .
   */
  public void dealAllCommonCards(ErrenTexapokerRoom.Table table, int state) {
    if (table.commonCards.size() != 5) {
      LOG.error("[二人德州]桌子[{}]底牌异常", table.getId());
      return;
    }
    List<Card> cardList = Lists.newArrayList();
    cardList.addAll(table.commonCards.subList(0, 5));
    msgMgr.sendCommonCard(table, state, cardList, 0);
  }

  /**
   * 一次性发最后两张牌 .
   */
  public void dealTurnAndRiver(ErrenTexapokerRoom.Table table, int state) {
    if (table.commonCards.size() != 5) {
      LOG.error("[二人德州]桌子[{}]底牌异常", table.getId());
      return;
    }
    List<Card> cardList = Lists.newArrayList();
    cardList.addAll(table.commonCards.subList(3, 5));
    msgMgr.sendCommonCard(table, state, cardList, 0);
  }

  /**
   * 弃牌结算 . ->不用比牌.
   *
   * @param table .
   */
  private void doFlopBalance(ErrenTexapokerRoom.Table table) {
    LOG.info("[二人德州]桌子[{}]弃牌结算了！！！", table.getId());
    saveBet(table);
    table.seats.stream().filter(seat -> seat.getPlayerId() > 0).forEach(seat -> {
      if (seat.isFold) {
        seat.billBet = 0;
      } else {
        seat.billBet = table.totalChips;
      }
    });

    errenTexapokerControl.banlanceControl(table);
    /**
     * 结算筹码计入总筹码 .
     */
    table.seats.stream().filter(seat -> seat.getPlayerId() > 0).forEach(seat -> {
      seat.setTotalChips(seat.getTotalChips() + seat.billBet);
    });
    msgMgr.sendBanlance(table, 1);
    billGold(table);
    resetTableData(table);
    table.seats.stream().filter(seat -> seat.getPlayerId() > 0).forEach(seat -> {
      // 触发房间大厅成员信息变更事件
      eventMgr.post(new RoomMemInfoUpdateEvent(playerMgr.getPlayer(seat.getPlayerId())));
    });
    table.state = 0;
    // resetReady(table);
  }

  /**
   * 输赢结果(发手牌的时候就已经知道结果了).
   *
   * @param table .
   */
  public void winResult(ErrenTexapokerRoom.Table table) {
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (!seat.isFold) {
        List<Card> cardList = Lists.newArrayList();
        cardList.addAll(seat.cards);
        cardList.addAll(table.commonCards);
        seat.handPower = handPowerRanker.rank(cardList);
      } else {
        seat.power = 0;
      }
    }
    table.seats.stream().filter(seat -> !seat.isFold && seat.getPlayerId() > 0).forEach(
        seat -> table.seats.stream().filter(seat1 -> !seat1.isFold && seat1.getPlayerId() > 0)
            .filter(seat1 -> seat.getOrder() != seat1.getOrder())
            .filter(seat1 -> seat.handPower.compareTo(seat1.handPower) >= 0)
            .forEach(seat1 -> seat.power++));

    int power = 0;
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (seat.getPlayerId() > 0) {
        if (seat.power > power) {
          power = seat.power;
        }
      }
    }
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (seat.power == power) {
        seat.isWin = 1;
      }
    }
  }

  /**
   * 正常结算 .
   */
  private void doBalance(ErrenTexapokerRoom.Table table) {
    table.seats.stream().filter(seat -> seat.getPlayerId() > 0).forEach(seat -> {
      seat.power = 0;
    });
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (!seat.isFold && seat.getPlayerId() > 0) {
        List<Card> cardList = Lists.newArrayList();
        cardList.addAll(seat.cards);
        if (cardList.size() == 2) {
          cardList.addAll(table.commonCards);
        }
        if (cardList.size() != 7) {
          LOG.info("Wrong!");
        }
        seat.handPower = handPowerRanker.rank(cardList);
      } else {
        seat.power = 0;
      }
    }
    table.seats.stream().filter(seat -> !seat.isFold && seat.getPlayerId() > 0).forEach(
        seat -> table.seats.stream().filter(seat1 -> !seat1.isFold && seat1.getPlayerId() > 0)
            .filter(seat1 -> seat.getOrder() != seat1.getOrder())
            .filter(seat1 -> seat.handPower.compareTo(seat1.handPower) >= 0)
            .forEach(seat1 -> seat.power++));
    int power = 0;
    /**
     * 拿到最大牌的power .
     */
    for (ErrenTexapokerRoom.Table.Seat seat : table.seats) {
      if (!seat.isFold) {
        if (seat.power > power) {
          power = seat.power;
        }
      }
    }
    int maxPower = power;
    saveBet(table);

    Map<Integer, Long> seatBet = samePowerBet(table);

    Map<Integer, Long> seatBillBet = Maps.newConcurrentMap();

    /**
     * 从最大的power开始结算(赢家结算) .
     */
    for (int i = 0; i < table.seats.size(); i++) {
      for (ErrenTexapokerRoom.Table.Seat seat1 : table.seats) { // 赢家
        if (seat1.isFold) {
          continue;
        }
        if (seat1.power == power) {
          if (seatBet.size() > 0 && seatBet.containsKey(seat1.power)) {
            if (seatBillBet.containsKey(seat1.power)) {
              continue;
            }
            for (Seat seat2 : table.seats) {
              if (seat1.power >= seat2.power) {
                long bet = seatBet.get(seat1.power);
                long billbet = 0;
                if (bet >= seat2.bet) {
                  billbet = seat2.bet;
                  seat2.bet = 0;
                } else {
                  billbet = bet;
                  seat2.bet -= bet;
                }
                if (!seatBillBet.containsKey(seat1.power)) {
                  seatBillBet.put(seat1.power, billbet);
                } else {
                  seatBillBet.put(seat1.power, seatBillBet.get(seat1.power) + billbet);
                }
              }
              if (table.exitChips > 0) {
                long bet = seatBet.get(seat1.power);
                long billbet = 0;
                if (seatBillBet.containsKey(seat1.power)) {
                  billbet = seatBillBet.get(seat1.power);
                }
                if (bet >= table.exitChips) {
                  billbet += table.exitChips;
                  table.exitChips = 0;
                } else {
                  billbet += seat1.bet;
                  table.exitChips -= seat1.bet;
                }
                seatBillBet.put(seat1.power, billbet);
              }
            }
            continue;
          }
          if (seatBet.size() > 0 && seatBet.containsKey(seat1.power)) {
            continue;
          }
          seat1.billBet = seat1.bet;
          // table.totalChips -= seat.bet;
          for (ErrenTexapokerRoom.Table.Seat seat2 : table.seats) {
            if (seat2.getOrder() != seat1.getOrder() && seat2.power < power) {
              if (seat2.bet >= seat1.bet) {
                seat1.billBet += seat1.bet;
                // int tempBet = seat2.bet - seat1.bet;x
                // seat2.bet = tempBet;
                seat2.bet = seat2.bet - seat1.bet;
              } else { // 否则赢家赢输家的所有
                seat1.billBet += seat2.bet;
                seat2.bet = 0;
              }
              if (table.exitChips > 0) {
                if (seat1.bet >= table.exitChips) {
                  seat1.billBet += table.exitChips;
                  table.exitChips = 0;
                } else {
                  seat1.billBet += seat1.bet;
                  table.exitChips -= seat1.bet;
                }
              }
            }
          }
        }
      }
      power -= 1;
      if (power < 0) {
        break;
      }
    }
    dealBet(seatBet, seatBillBet, table, maxPower);
    // samePower(table);

    /**
     * 将结算筹码计入总筹码数 .
     */
    errenTexapokerControl.banlanceControl(table);
    table.seats.stream().filter(seat -> seat.getPlayerId() > 0).forEach(seat -> {
      seat.setTotalChips(seat.getTotalChips() + seat.billBet);
    });
    msgMgr.sendBanlance(table, 0);
    billGold(table);
    resetTableData(table);
    table.state = 0;

    // 发送成员变更消息
    for (Seat seat : table.seats) {
      seat.setState(SeatState.SEATED);
      // 触发房间大厅成员信息变更事件
      eventMgr.post(new RoomMemInfoUpdateEvent(playerMgr.getPlayer(seat.getPlayerId())));
    }
    // resetReady(table);
  }

  private Map<Integer, Long> samePowerBet(Table table) {
    Map<Integer, Long> seatBet = Maps.newConcurrentMap();
    for (Seat seat : table.seats) {
      if (seat.getPlayerId() == 0) {
        continue;
      }
      if (seat.power == 0) {
        continue;
      }
      if (seat.isFlag) {
        continue;
      }
      for (Seat seat1 : table.seats) {
        if (seat1.getPlayerId() == 0) {
          continue;
        }
        if (seat1.power == 0) {
          continue;
        }
        if (seat1.getOrder() == seat.getOrder()) {
          continue;
        }
        if (seat1.power == seat.power) {
          if (!seatBet.containsKey(seat.power)) {
            long bet = seat1.tempbet + seat.tempbet;
            seatBet.put(seat.power, bet);
          } else {
            long bet = seatBet.get(seat.power) + seat1.tempbet;
            seatBet.put(seat.power, bet);
          }
          seat1.isFlag = true;
        }
      }
    }
    return seatBet;
  }

  private void dealBet(Map<Integer, Long> seatBet, Map<Integer, Long> seatBillBet, Table table,
      int maxPower) {
    for (Seat seat : table.seats) {
      if (seatBet.containsKey(seat.power) && seatBillBet.containsKey(seat.power)) {
        if (seat.power == maxPower && seatBillBet.get(seat.power) == 0) {
          seat.billBet = table.totalChips * seat.tempbet / seatBet.get(seat.power);
        } else {
          seat.billBet =
              seatBillBet.get(seat.power) * seat.tempbet / seatBet.get(seat.power) + seat.bet;
        }
      }
    }
  }

  /**
   * 将玩家下注信息保存.
   *
   * @param table .
   */
  private void saveBet(Table table) {
    table.seats.stream().filter(seat -> seat.getPlayerId() > 0).forEach(seat -> {
      seat.tempbet = seat.bet;
    });

  }

  /**
   * 玩家请求亮牌 .
   *
   * @param player 玩家.
   * @param roomId .
   * @param tableId .
   */
  public void lightCard(Player player, int roomId, int tableId) {
    if (player == null) {
      return;
    }
    ErrenTexapokerRoom.Table table = dataMgr.getTable(tableId);
    ErrenTexapokerRoom.Table.Seat seat = dataMgr.getPlayerSeat(player.getId());
    if (table == null || seat == null) {
      return;
    }
    msgMgr.sendLighCard(table, seat);
  }

  /**
   * 快速进入牌桌.
   * 
   * @param player 玩家.
   * @param roomId .
   */
  public void fastEnterTable(Player player, int roomId) {
    ErrenTexapokerRoom room = dataMgr.getRoom(roomId);
    if (room == null) {
      LOG.error("[二人德州]玩家[{}][{}]进入的房间[{}]不存在", player.getPlayerName(), player.getId(), roomId);
      return;
    }

    ErrenTexapokerRoom.Table.Seat playerSeat = dataMgr.getPlayerSeat(player.getId());
    if (playerSeat != null) {
      LOG.error("[二人德州]玩家[{}][{}]已经在房间[{}]中的桌子[{}]上,不能进入其他桌子", player.getPlayerName(),
          player.getId(), playerSeat.table.room.getId(), playerSeat.table.getId());
      return;
    }

    ErrentexaporkerRoomDomain roomDom = roomDic.get(roomId);
    if (player.getGold() < roomDom.getLower()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_NOT_ENOUGH, Operator.SYSTEM);
      LOG.warn("[二人德州]玩家[{}][{}]进入的房间[{}]低于下限[{}]金币", player.getPlayerName(), player.getId(),
          roomId, roomDom.getLower());
      return;
    }

    if (player.getGold() > roomDom.getUpper()) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.PLAYER_GOLD_BEYOND_MAX_GOLD, Operator.SYSTEM);
      LOG.warn("[二人德州]玩家[{}][{}]进入的房间[{}]高于上限[{}]金币", player.getPlayerName(), player.getId(),
          roomId, roomDom.getUpper());
      return;
    }
    // 台费(金币)
    int afee = roomDom.getAfee();
    if (player.getGold() < afee) {
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_NOT_ENOUGH_FEE, Operator.SYSTEM);
      LOG.warn("[二人德州]玩家[{}][{}]进入桌子金币不足台费", player.getPlayerName(), player.getId());
      return;
    }

    ErrenTexapokerRoom.Table.Seat emptSeat = findEmptySeat(player, room);
    if (emptSeat != null) {
      if (!gameHallMgr.checkTableSetting(player, emptSeat.table(), null)) {
        return;
      }
      if (player.getGold() < afee) {
        LOG.error("[二人德州]玩家[{}][{}]进入的牌桌位置[]失败，没能支付台费", player.getPlayerName(), player.getId(),
            emptSeat.getOrder());
        return;
      }
      playerMgr.minusGold(player, afee, LogReason.ERREN_TEXAPOKER_ENTER_TABLE_MINUS_AFEE);
      DbLogService.log(new GameAfeeLog(player, emptSeat.table(), afee));
      emptSeat.setPlayerId(player.getId());
      emptSeat.setPlayerName(player.getPlayerName());
      emptSeat.setState(SeatState.SEATED);
      emptSeat.setRobot(player.playerBo().isRobot());
      dataMgr.updatePlayerSeats(player.getId(), emptSeat);
      eventMgr.post(new EnterTableEvent(player, emptSeat));
      msgMgr.sendEnterTableMsg(player, emptSeat.table);
      msgMgr.sendOhterPlayer(emptSeat.table, emptSeat);
      LOG.info("[二人德州]玩家[{}][{}]进快速入桌子[{}]座位[{}]成功", player.getPlayerName(), player.getId(),
          emptSeat.table.getId(), emptSeat.getOrder());
      int readyTime = timeDic.get(1).getTime();
      emptSeat.stepFuture = scheduleMgr.schedule(() -> exitTable(player), readyTime + ADDED_DELAY,
          TimeUnit.SECONDS, gameExecutor);

      return;
    }

    // 提示没有空位置
    commonMsgMgr.sendTipDlgMsg(player, ErrorCode.GAME_NOT_SIT, Operator.SYSTEM);
    // 发送快速进入桌子结果
    msgMgr.sendFastEnterTableMsg(player, 1);
    LOG.info("[二人德州]当前没有空位，玩家[{}][{}]快速进入失败", player.getPlayerName(), player.getId());
  }

  /**
   * 查找空位置,优先查找有人的桌子.
   * 
   * @param player 玩家.
   * @param room .
   * @return
   */
  private Seat findEmptySeat(Player player, ErrenTexapokerRoom room) {
    Seat emptySeat = null;
    ArrayList<Table> tables = new ArrayList<>(room.tables.values());
    Collections.shuffle(tables);

    for (Table table : tables) {
      if (table.isGaming) {
        continue;
      }

      if (table.getPassword() != null) {
        continue;
      }

      if (table.getLimitIp() != null && table.getLimitIp().equals(player.ip)) {
        continue;
      }

      if (player.getGold() < table.getLimitGold()) {
        continue;
      }

      boolean hasPlayer = false;
      Seat tableEmptySeat = null;
      for (Seat seat : table.seats) {
        if (seat.getPlayerId() == 0) {
          emptySeat = seat;
          tableEmptySeat = seat;
        } else {
          hasPlayer = true;
        }
      }

      if (hasPlayer && tableEmptySeat != null) {
        return tableEmptySeat;
      }
    }

    return emptySeat;
  }

  /**
   * 玩家筹码兑换金币.
   * 
   * @param player 玩家.
   * @param chips .
   */
  public void exchangeGold(Player player, long chips) {
    if (player == null) {
      return;
    }
    ErrenTexapokerRoom.Table.Seat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[二人德州]玩家[{}][{}]不在牌桌中不能兑换金币", player.getPlayerName(), player.getId());
      return;
    }

    if (seat.getTotalChips() < chips) {
      LOG.error("[二人德州]玩家[{}][{}]兑换的筹码[{}]超过自己的筹码总和[{}]", player.getPlayerName(), player.getId(),
          chips, seat.getTotalChips());
      return;
    }
    long gold = convertGold(seat.table.room.getId().intValue(), chips);
    minusChips(seat, chips);
    LOG.info("[二人德州]玩家[{}][{}]用筹码[{}]兑换金币[{}]成功", player.getPlayerName(), player.getId(), chips,
        gold);
    // 触发房间大厅成员信息变更事件
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
  }

  /**
   * 兑换筹码的金币 . 玩家金币兑换筹码.
   * 
   * @param player 玩家.
   * @param gold .
   */
  public void exchangeChips(Player player, long gold) {
    if (player == null) {
      return;
    }
    ErrenTexapokerRoom.Table.Seat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[二人德州]玩家[{}][{}]不在牌桌中不能兑换筹码", player.getPlayerName(), player.getId());
      return;
    }

    if (player.getGold() < gold
        + convertGold(seat.table.room.getId().intValue(), seat.getTotalChips())) {
      LOG.error("[二人德州]玩家[{}][{}]兑换的金币[{}]加上筹码[{}]兑换的金币超过自己的金币总和[{}]", player.getPlayerName(),
          player.getId(), gold, seat.getTotalChips(), player.getGold());
      return;
    }
    long chips = convertChip(seat.table.room.getId().intValue(), gold);
    addChips(seat, chips);
    LOG.info("[二人德州]玩家[{}][{}]用金币[{}]兑换筹码[{}]成功", player.getPlayerName(), player.getId(), gold,
        chips);
    eventMgr.post(new RoomMemInfoUpdateEvent(player));
  }

  /**
   * 增加筹码.
   * 
   * @param seat 座位信息.
   * @param chips .
   */
  private void addChips(ErrenTexapokerRoom.Table.Seat seat, long chips) {
    seat.setTotalChips(seat.getTotalChips() + chips);
    msgMgr.sendChipsChangeMsg(seat);
  }

  /**
   * 减少筹码 .
   * 
   * @param seat 座位信息.
   * @param chips .
   */
  public void minusChips(ErrenTexapokerRoom.Table.Seat seat, long chips) {
    seat.setTotalChips(seat.getTotalChips() - chips);
    msgMgr.sendChipsChangeMsg(seat);
  }

  /**
   * 筹码换算金币.
   * 
   * @param roomId .
   * @param chips .
   */
  public long convertGold(int roomId, long chips) {
    ErrentexaporkerRoomDomain roomDomain = roomDic.map().get(roomId);
    return chips * roomDomain.getProportionGold() / roomDomain.getProportionChips();
  }

  /**
   * 筹码换算金币, . 记录日志需要的金币 .
   * 
   * @param roomId .
   * @param chips .
   */
  public double convertLogGold(int roomId, long chips) {
    ErrentexaporkerRoomDomain roomDomain = roomDic.map().get(roomId);

    return (double) chips * roomDomain.getProportionGold() / roomDomain.getProportionChips();
  }

  /**
   * 金币换算筹码 .
   * 
   * @param roomId .
   * @param gold .
   * @return
   */
  private long convertChip(int roomId, long gold) {
    ErrentexaporkerRoomDomain roomDomain = roomDic.map().get(roomId);

    return gold * roomDomain.getProportionChips() / roomDomain.getProportionGold();
  }

  /**
   * 玩家退出房间 .
   * 
   * @param player 玩家.
   */
  public void exitRoom(Player player) {
    AbstractRoom curRoom = player.curRoom;
    ErrenTexapokerRoom.Table.Seat seat = dataMgr.getPlayerSeat(player.getId());

    if (curRoom instanceof ErrenTexapokerRoom) {
      if (seat == null) {
        player.curRoom = null;
        ErrenTexapokerRoom room = (ErrenTexapokerRoom) curRoom;
        room.getPlayers().remove(player.getId());
        msgMgr.sendExitRoomMsg(player, 0);
        LOG.info("[二人德州]玩家[{}][{}]退出房间[{}]", player.getPlayerName(), player.getId(), room.getId());
      } else {
        LOG.error("[二人德州]玩家[{}][{}]在座位中不能退出房间,请先退出桌子", player.getPlayerName(), player.getId());
        msgMgr.sendExitRoomMsg(player, 1);
        return;
      }
      eventMgr.post(new ExitRoomEvent(player, curRoom));
    } else {
      LOG.warn("[二人德州]玩家[{}][{}]不在德州扑克房间中，无法退出房间", player.getPlayerName(), player.getId());
      msgMgr.sendExitRoomMsg(player, 1);
    }
  }

  /**
   * 玩家退出牌桌 .
   *
   * @param player 玩家.
   */
  public void exitTable(Player player) {
    ErrenTexapokerRoom.Table.Seat seat = dataMgr.getPlayerSeat(player.getId());
    if (seat == null) {
      LOG.error("[二人德州]玩家[{}][{}]未在牌桌里不能退出", player.getPlayerName(), player.getId());
      return;
    }
    seat.stepFuture.cancel(false);
    if (seat.table.isGaming) {
      doFold(seat, true);
      billGold(seat);
    }
    doExitTable(player, seat);
  }

  /**
   * 退出桌子 .
   * <p/>
   * . 1.玩家正常退出牌桌, . 能在在线玩家中拿到player, 正常退出桌子
   * <p/>
   * . 2.玩家非正常退出(如掉线)后,从活跃玩家或数据库中拿到PlayerDomain，结算后马上更新缓冲和数据库 .
   * 
   * @param seat 座位信息.
   * 
   */

  private void doExitTable(Player player, ErrenTexapokerRoom.Table.Seat seat) {
    msgMgr.sendExitTableMsg(seat);
    LOG.info("[二人德州]玩家[{}][{}]退出桌子[{}]", seat.getPlayerId(), seat.getPlayerName(),
        seat.table.getId());
    if (seat.table.isGaming) {
      if (checkIsAllExitTable(seat.table, seat)) {
        seat.table.isGaming = false;
      }
    }
    if (seat.ready) {
      seat.table.readyCount -= 1;
      seat.table.readyCount = seat.table.readyCount < 0 ? 0 : seat.table.readyCount;
    } else {
      if (!seat.table.isGaming) {
        seat.clear();
        readyDealCards(seat.table, player, true);
      }
    }
    if (seat.table.isGaming) {
      seat.table.exitChips += seat.bet;
    }
    dataMgr.removePlayerSeat(player.getId());
    seat.clear();
    eventMgr.post(new ExitTableEvent(player, seat));
  }

  /**
   * 检测是否所有人都强行退出了桌子 . ->所有人强行退出桌子，该桌子的游戏状态重置 .
   *
   * @param table .
   * @param seat 座位信息.
   * @return
   */
  public boolean checkIsAllExitTable(Table table, Seat seat) {
    boolean flag = true;
    for (ErrenTexapokerRoom.Table.Seat seat1 : table.seats) {
      if (seat1.getPlayerId() > 0 && seat.getOrder() != seat1.getOrder()) {
        flag = false;
        break;
      }
    }
    return flag;
  }

  /**
   * 重置牌桌数据.
   * 
   * @param table .
   */
  private void resetTableData(ErrenTexapokerRoom.Table table) {
    table.reset();
    table.seats.forEach(ErrenTexapokerRoom.Table.Seat::resetButTotalChips);
  }

  /**
   * 退出游戏大厅 .
   * 
   * @param player 玩家.
   */
  public void exitGameHall(Player player) {
    if (player.curRoom instanceof ErrenTexapokerRoom) {
      LOG.error("[二人德州]玩家[{}][{}]在房间中不能退出大厅", player.getPlayerName(), player.getId());
      return;
    }
    LOG.info("[二人德州]玩家[{}]退出房大厅成功", player.getId());
  }

  /**
   * 玩家进入游戏大厅 .
   * 
   * @param player 玩家.
   */
  public void enterGameHall(Player player) {
    if (player.curRoom != null) {
      Game game = player.curRoom.game();
      LOG.warn("[二人德州]玩家[{}][{}]在[{}]游戏中，暂时无法进入游戏", player.getId(), player.getPlayerName(),
          game.getDesc());
      commonMsgMgr.sendErrorDlgMsg(player, ErrorCode.GAME_ALREADY_IN, Operator.SYSTEM,
          game.getDesc());
      return;
    }
    msgMgr.sendGameHallMsg(player);

    LOG.info("[二人德州]玩家[{}][{}]进入大厅成功", player.getPlayerName(), player.getId());
  }

  /**
   * 结算金币 .
   *
   * @param table .
   */
  private void billGold(Table table) {
    int roomId = table.room.getId().intValue();
    for (Seat seat : table.seats) {
      if (seat.getPlayerId() == 0) {
        continue;
      }

      long chips = seat.billBet > 0 ? seat.billBet - seat.tempbet : -seat.tempbet;
      long gold = convertGold(roomId, chips);

      Player player = playerMgr.getPlayer(seat.getPlayerId());
      PlayerBo playerDom = playerMgr.selectPlayer(seat.getPlayerId());
      if (player != null) {
        playerMgr.addGold(player, gold, LogReason.ERREN_TEXAPOKER_BILL);
      } else {
        playerMgr.addGold(playerDom, gold, LogReason.ERREN_TEXAPOKER_BILL);
      }
      LOG.info("[二人德州]]]玩家[{}][{}]结算筹码[{}]",
          player != null ? player.getPlayerName() : playerDom.getPlayerName(),
          player != null ? player.getId() : playerDom.getId(), chips);
      DbLogService.log(new Game15WinLoseLog(playerMgr.selectPlayer(seat.getPlayerId()), seat, chips,
          gold, LogReason.ERREN_TEXAPOKER_BILL));
    }
  }

  /**
   * 结算金币 .
   *
   * @param seat 座位信息.
   */
  private void billGold(Seat seat) {
    int roomId = seat.table.room.getId().intValue();
    long chips = seat.billBet > 0 ? seat.billBet - seat.bet : -seat.bet;
    long gold = convertGold(roomId, chips);
    Player player = playerMgr.getPlayer(seat.getPlayerId());
    PlayerBo playerDom = playerMgr.selectPlayer(seat.getPlayerId());
    if (player != null) {
      playerMgr.addGold(player, gold, LogReason.ERREN_TEXAPOKER_BILL);
    } else {
      playerMgr.addGold(playerDom, gold, LogReason.ERREN_TEXAPOKER_BILL);
    }

    LOG.info("[二人德州]]]玩家[{}][{}]结算筹码[{}]",
        player != null ? player.getPlayerName() : playerDom.getPlayerName(),
        player != null ? player.getId() : playerDom.getId(), chips);
    DbLogService.log(new Game15WinLoseLog(playerMgr.selectPlayer(seat.getPlayerId()), seat, chips,
        gold, LogReason.ERREN_TEXAPOKER_BILL));
  }

  /**
   * 玩家在游戏中卡住，游戏无法正常进行下去，GM操作强制清除该桌数据 .
   * 
   * @param playerId 玩家id.
   */
  private void doClearGameData(long playerId) {
    try {
      dataMgr.removePlayerSeat(playerId);
      /*
       * 清除玩家房间数据 .
       */
      for (ErrenTexapokerRoom room : dataMgr.getRooms().values()) {
        room.getPlayers().remove(playerId);

        /*
         * 清除房间中玩家所在桌子数据 .
         */
        for (Table table : room.tables.values()) {
          // 玩家在牌桌标识
          boolean inTable = false;
          for (Seat seat : table.seats) {
            if (seat.getPlayerId() == playerId) {
              inTable = true;
              break;
            }
          }

          if (inTable) {
            table.reset();
            for (Seat seat : table.seats) {
              dataMgr.removePlayerSeat(seat.getPlayerId());
              room.getPlayers().remove(seat.getPlayerId());
              Player player = playerMgr.getPlayer(seat.getPlayerId());
              seat.clear();
              if (player != null) {
                loginMgr.noticeLogoutForGameExit(player);
              }
            }
          }
        }
      }

      Player player = playerMgr.getPlayer(playerId);
      if (player != null) {
        loginMgr.noticeLogoutForGameExit(player);
      }
    } catch (Exception e) {
      LOG.error("[二人德州]清除玩家[" + playerId + "]卡限失败", e);
    }
  }

  /**
   * 公牌 .
   *
   * @return
   */
  // private List<Card> testCommonCard() {
  // List<Card> list = Lists.newArrayList();
  // Card card = new Card(CardSuit.DIAMOND, CardNumber.SEVEN);
  // Card card1 = new Card(CardSuit.SPADE, CardNumber.SEVEN);
  // Card card3 = new Card(CardSuit.HEART, CardNumber.KING);
  // Card card4 = new Card(CardSuit.HEART, CardNumber.QUEEN);
  // Card card5 = new Card(CardSuit.SPADE, CardNumber.EIGHT);
  // list.add(card);
  // list.add(card1);
  // list.add(card3);
  // list.add(card4);
  // list.add(card5);
  // return list;
  // }
  //
  // /**
  // * 公牌 .
  // *
  // * @return
  // */
  // private List<Card> testHandCard() {
  // List<Card> list = Lists.newArrayList();
  // Card card = new Card(CardSuit.CLUB, CardNumber.NINE);
  // Card card1 = new Card(CardSuit.HEART, CardNumber.THREE);
  // list.add(card);
  // list.add(card1);
  // return list;
  // }
  //
  // private List<Card> testHandCard1() {
  // List<Card> list = Lists.newArrayList();
  // Card card = new Card(CardSuit.SPADE, CardNumber.FOUR);
  // Card card1 = new Card(CardSuit.HEART, CardNumber.FOUR);
  // list.add(card);
  // list.add(card1);
  // return list;
  // }

}
